/** \file    file_dir.h
  * \brief   Filename manipulations and directory control routines.
  * \date    2002 - 2004
  * \author  Fedor Chelnokov
  * \version $Header: /home/cscom1r4/cvsroot/cs/include/mswin/file_dir.h,v 1.5 2004/05/23 12:28:42 cscom1r4 Exp $
  */

#ifndef _file_dir_h_
#define _file_dir_h_

#include <string>
#include <assert.h>

namespace mswin {
 
///return true if file was successfully deleted
bool remove(const char *f);
inline bool remove(const std::string &fn)
	{ return remove(fn.c_str()); }

///excluding directories
bool file_exists(const char *);
inline bool file_exists(const std::string &fn)
	{ return file_exists(fn.c_str()); }

///files and directories
bool exists(const char *);
inline bool exists(const std::string &fn)
	{ return exists(fn.c_str()); }

bool readable(const char *);
inline bool readable(const std::string &fn)
	{ return readable(fn.c_str()); }

bool writable(const char *);
inline bool writable(const std::string &fn)
	{ return writable(fn.c_str()); }

///returns name of file without path and extension
std::string basename(const std::string & fn);
///returns name of file without path
std::string filename(const std::string & fn);

///get the current working directory
std::string getcwd();

///sets the current working directory, then restores the previous
class cwd {
public:
	cwd(const std::string & dir);
	~cwd();
private:
	std::string _temp;
};

void rename(const std::string & oldname, const std::string & newname);

///return true if directory was created or already existed
bool mkdir(const char * name);
inline bool mkdir(const std::string & name)
	{ return mkdir(name.c_str()); }

///ensures that directory path is ended with slash or backslash
inline void slash_ended(std::string & dir) {
	if (dir.empty())
		return;
	char last = dir[dir.size()-1];
	if (last != '/' && last != '\\')
		dir += '\\';
}

///changes extension of a file with given path
void change_ext(std::string & path, const std::string & newext);

///returns true if path ends up with given extension (ext may have or may have not leading dot)
bool equal_ext(const std::string & path, const std::string & ext);

///returns composed name of 'file'.'ext' in 'subdir' of base 'dir'
std::string subdir_file(const std::string &dir, const std::string &subdir, 
			bool create_subdir = false, ///< guaranties existance of subdir
			const std::string &basename = std::string(), const std::string &ext = std::string());

inline void mksubdir(const std::string &dir, const std::string &subdir)
	{ subdir_file(dir, subdir, true); }

/// a std::string version of _splitpath
void splitpath(const std::string & path, std::string * pdrive, std::string * pdir, std::string * ptitle, std::string * pext);

/// a std::string version of _makepath
std::string makepath(const std::string * pdrive, const std::string * pdir, const std::string * ptitle, const std::string * pext);

/// create an absolute or full path name for the specified relative path name
char * fullpath(char * abs_path, const char * rel_path, size_t max_length);
/// a std::string versions of fullpath
std::string fullpath(const char * rel_path);
inline std::string fullpath(const std::string & rel_path)
	{ return fullpath(rel_path.c_str()); }

/// returns path to the parent directory of given file or directory
std::string parent(const std::string & path);

/// returns name of given file with extension without all parent directories
std::string child(const std::string & path);

/// creates all necessary subdirectories until the given path is valid
bool createpath(std::string path);

/// given an absolute path, returns path relative to the base
std::string relative(std::string path, std::string base);

/// given a path relative to the base, returns absolute path
std::string absolute(std::string path, std::string base);

/// returns true if both drives share the same drive or both miss drive specification
bool same_drive(const std::string & path1, const std::string & path2);

/// replaces all '/' by '\\', and ruen all latin letters in lower case
void standardize_path(std::string & path);

/// returns true if path start from (back)slash or from drive letter preffix
bool is_absolute(std::string const & path);

///iterator-like class that enumerates files and directories specified by a string with wildcards
class enum_dir {
public:
	enum_dir() : _open(false) {}
	enum_dir(const std::string & filespec) : _open(false)
		{ open(filespec); }
	~enum_dir()
		{ close(); }

	void open(const std::string & filespec);
	void close();

	//iteration
	enum_dir & operator ++() {
		assert(_open);
		findnext();
		return * this;
	}
	bool end() const
		{ return !_open; }

	///get the name of current file
	const std::string & name() const {
		assert(_open);
		return _name;
	}
	///is current item a directory?
	bool is_dir() const {
		assert(_open);
		return _dir;
	}
	bool is_special() const {
		return _name == "." || _name == "..";
	}

protected:
	void findnext();

private:
	bool		_open;
	long		_handle;
	std::string _name;
	bool		_dir;
};

///enumerates files only
class enum_file : public enum_dir {
public:
	enum_file(const std::string & filespec)
		{ open(filespec); }

	void open(const std::string & filespec);

	//iteration
	enum_file & operator ++() {
		findnext();
		return * this;
	}
protected:
	///skips subdirs
	void findnext();
};

/**
 * A pattern consists of normal characters, which match themselves, and meta characters. 
 * The meta characters are "*", "?". These characters lose there special meanings 
 * if they are quoted ("). 
 * 
 * An asterisk ("*") matches any string of characters. 
 * A question mark matches any single character. 
 *
 * The matching is case sensitive!
 */
bool pattern_match(const std::string & name, const std::string & pattern);

} //namespace mswin

#endif //_file_dir_h_
